package rs.etf.kdp.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Information about computer that is connected to the server
 * @author Djordje Vukovic
 * @version 1.0
 * @created 20-May-2011 2:30:15 AM
 */
public class Node extends Thread{

	private int currentlyRunningThreads;
	private int possibleThreads;
	//private Socket socketToNode;
	private String name; // 0 client 1 workstation XXX
	private String ipAddress;
	private List<Simulation> simulationsRunning;
	private ObjectInputStream input;
	private ObjectOutputStream output;
	private InterthreadBuffer ithread; 
	//private Heartbeater heartbeater;
	private HashMap<Long, InterthreadBuffer> communicationBuffer;
	private SendPermission permission;

	public Node(String name, int possibleT, String ip, ObjectInputStream in, ObjectOutputStream out, Heartbeater hb){
		communicationBuffer = new HashMap<Long, InterthreadBuffer>();
		currentlyRunningThreads = 0;
		possibleThreads = possibleT;
		//socketToNode = s;
		this.name = name;
		ipAddress = ip;
		simulationsRunning = new ArrayList<Simulation>();
		input = in;
		output = out;
		//heartbeater = hb;
		permission = new SendPermission();
	}
	
	public void run()
	{ 
		synchronized(this)
		{
			while(true)
			{
				String command = null;

				try {
					command = (String)input.readObject();
				} catch (IOException e) {
					System.out.println("Node disconnected");
					return;
				} catch (ClassNotFoundException e) {
					System.out.println("ClassNotFound in Node");
				}

				//			if(command.startsWith("heartbeat_answer"))
				//			{
				//				respondToHeartbeater(command);
				//			} else 

				if(command.startsWith("send_part"))
				{
					String[] msgTok = command.split(" ");
					long id = Long.parseLong(msgTok[1]);
					InterthreadBuffer b = communicationBuffer.get(id);
					b.putEventFromWS(new EventFromWS(null, msgTok[0]));
				} else if(command.startsWith("workstation transport"))
				{
					EventFromWS wsevent = null;
					String[] tok = command.split(" ");
					long id = Long.parseLong(tok[3]);
					try {
						wsevent = (EventFromWS)input.readObject();
					} catch (Exception e) {
						System.out.println("Cast to EventFromWS in Node");
					}

					InterthreadBuffer b = communicationBuffer.get(id);
					wsevent.setMessage(command);
					b.putEventFromWS(wsevent);
				} else if(command.startsWith("result"))
				{
					currentlyRunningThreads--;
					String[] tok = command.split(" ");
					String[][] result = null;

					try {
						result = (String[][])input.readObject();
					} catch (IOException e) {
						System.out.println("IOException while receiving restult");
					} catch (ClassNotFoundException e) {
						System.out.println("ClassNotFound while receiving result");
					}

					Simulation toAddResult = findSimulationById(Long.parseLong(tok[1]));
					toAddResult.getResults().insertResult(result);

					if(toAddResult.getResults().isCompleted())
					{
						//send to client
						InterthreadBuffer b = communicationBuffer.get(Long.parseLong(tok[1]));
						EventFromWS toJobManager = new EventFromWS();
						toJobManager.setMessage("result " + tok[1]);
						b.putEventFromWS(toJobManager);

					}

					//get nodes that simulated this simulation
					if(!toAddResult.getSimulationStatus().equals("DONE"))
					{
						toAddResult.setSimulationStatus("DONE");
						toAddResult.requestResults();			
					}

				}
			}
		}
	}
	
//	public void respondToHeartbeater(String msg)
//	{
//		String[] tok = msg.split(" ");
//		String ip = tok[1];
//		List<NodeInfo> hblist = heartbeater.getNodesStatus();
//		
//		for(int i=0; i<hblist.size(); i++)
//		{
//			NodeInfo ni = hblist.get(i);
//			if(ni.getIpAddress().equals(ip))
//			{
//				ni.setOk(true);
//			}
//		}
//	}
	
	public void sentStringMessage(String msg) throws IOException
	{
		output.writeObject(msg);
	}
	
	public InterthreadBuffer getInterthread() {
		return ithread;
	}

	public void setInterthread(InterthreadBuffer ithread) {
		this.ithread = ithread;
	}

	public ObjectInputStream getInput() {
		return input;
	}

	public void setInput(ObjectInputStream input) {
		this.input = input;
	}

	public ObjectOutputStream getOutput() {
		return output;
	}

	public void setOutput(ObjectOutputStream output) {
		this.output = output;
	}

	public void addSimulation(Simulation newSim)
	{
		simulationsRunning.add(newSim);
	}
	
	public void removeSimulation(Simulation remSim)
	{
		simulationsRunning.remove(remSim);
	}

//	public boolean isClient(){
//		if(type == 0) return true;
//		else return false;
//	}
//
//	public boolean isWorkstation(){
//		if(type == 1) return true;
//		else return false;
//	}
//
//	public void setTypeClient(){
//		type = 0;
//	}
//
//	public void setTypeWorkstation(){
//		type = 1;
//	}

	public void insertInterthreadBuffer(long jobId, InterthreadBuffer buff)
	{
		communicationBuffer.put(jobId, buff);
	}
	
	public void removeInterthreadBuffer(long id)
	{
		communicationBuffer.remove(id);
	}

	public int getCurrentlyRunningThreads() {
		return currentlyRunningThreads;
	}


	public InterthreadBuffer getIthread() {
		return ithread;
	}

	public void setIthread(InterthreadBuffer ithread) {
		this.ithread = ithread;
	}

	public String getNameOfNode() {
		return name;
	}

	public void setNameOfNode(String name) {
		this.name = name;
	}

	public void setCurrentlyRunningThreads(int currentlyRunningThreads) {
		this.currentlyRunningThreads = currentlyRunningThreads;
	}


	public int getPossibleThreads() {
		return possibleThreads;
	}


	public void setPossibleThreads(int possibleThreads) {
		this.possibleThreads = possibleThreads;
	}
	
	public void incrementNumberOfJobs() throws AssignJobToBusyNode
	{
		currentlyRunningThreads++;
		if(currentlyRunningThreads >= possibleThreads) throw new AssignJobToBusyNode(possibleThreads, currentlyRunningThreads);
	}
	
	public void decrementNumberOfJobs() throws AssignJobToBusyNode
	{
		currentlyRunningThreads--;
		if(currentlyRunningThreads < 0) throw new AssignJobToBusyNode(possibleThreads, currentlyRunningThreads);
	}


	public String getIpAddress() {
		return ipAddress;
	}


	public void setIpAddress(String ipAddress) {
		this.ipAddress = ipAddress;
	}

	public List<Simulation> getSimulationsRunning() {
		return simulationsRunning;
	}

	public void setSimulationsRunning(List<Simulation> simulationsRunning) {
		this.simulationsRunning = simulationsRunning;
	}

	public HashMap<Long, InterthreadBuffer> getCommunicationBuffer() {
		return communicationBuffer;
	}

	public void setCommunicationBuffer(
			HashMap<Long, InterthreadBuffer> communicationBuffer) {
		this.communicationBuffer = communicationBuffer;
	}
	
	private Simulation findSimulationById(long id)
	{
		for(Simulation s : simulationsRunning)
		{
			if(s.getJob().getServerID()==id) return s;
		}
		
		return null;
	}

	public SendPermission getPermission() {
		return permission;
	}

	public void setPermission(SendPermission permission) {
		this.permission = permission;
	}
	
	
}